import java.util.Random;

import junit.framework.TestCase;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class MyPriorityQueueTest extends TestCase {
	private MyPriorityQueue queue;

	private MyComparator comparator;
	private Random random;

	@Before
	protected void setUp() throws Exception {
		super.setUp();
		comparator = new MyComparator();
		random = new Random();
		int size = 1 + random.nextInt(100);
		queue = new MyPriorityQueue(size, comparator);

	}

	@After
	protected void tearDown() throws Exception {
		super.tearDown();
	}

	/*******************************************************************
	 * Cause-Effect Analysis for offer()
	 *******************************************************************/

	/**
	 * if e is null, return NullPointerException
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAOfferNullReturnNullPointerException() {
		boolean exceptionThrown = false;
		try {
			queue.offer(null);
		} catch (NullPointerException ex) {
			exceptionThrown = true;
		}
		assertTrue(exceptionThrown);
	}

	/**
	 * random comparable e, return true
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAOffer() {
		int size = queue.size();
		IntegerElement integerElement = new IntegerElement(
				random.nextInt(Integer.MAX_VALUE - 1));
		assertTrue(queue.offer(integerElement));
		assertEquals(++size, queue.size());
		assertEquals(integerElement.getValue(),
				((IntegerElement) queue.peek()).getValue());

		// add an element that lesser than the previous element, the peek method
		// should return the lesser element
		IntegerElement lesserIntegerElement = new IntegerElement(
				integerElement.getValue() - 1);
		assertTrue(queue.offer(lesserIntegerElement));
		assertEquals(++size, queue.size());
		assertEquals(lesserIntegerElement.getValue(),
				((IntegerElement) queue.peek()).getValue());

		// add an element that greater than the previous element, the peek
		// method
		// should return the least element
		IntegerElement greaterIntegerElement = new IntegerElement(
				integerElement.getValue() + 1);
		assertTrue(queue.offer(greaterIntegerElement));
		assertEquals(++size, queue.size());
		assertEquals(lesserIntegerElement.getValue(),
				((IntegerElement) queue.peek()).getValue());
	}

	/**
	 * if e is not comparable, return ClassCastException
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAOfferNotComparableReturnClassCastException() {
		// add one integer element first
		queue.offer(new IntegerElement(random.nextInt(Integer.MAX_VALUE)));
		// then add another String element to cause the exception to be thrown
		StringElement stringE = new StringElement("abc");
		boolean exceptionThrown = false;
		try {
			queue.offer(stringE);
		} catch (ClassCastException ex) {
			exceptionThrown = true;
		}
		assertTrue(exceptionThrown);
	}

	/*******************************************************************
	 * Induction testing for offer()
	 *******************************************************************/
	/**
	 * testing with an empty queue
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAInductionOfferWithEmptyQueue() {
		int size = queue.size();
		IntegerElement integerElement = new IntegerElement(
				random.nextInt(Integer.MAX_VALUE - 1));
		assertTrue(queue.offer(integerElement));
		assertEquals(++size, queue.size());
		assertEquals(integerElement.getValue(),
				((IntegerElement) queue.peek()).getValue());
	}

	/**
	 * testing with an one-element queue
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAInductionOfferWithOneElementQueue() {
		int size = queue.size();
		IntegerElement integerElement = new IntegerElement(
				random.nextInt(Integer.MAX_VALUE));
		assertTrue(queue.offer(integerElement));
		++size;

		IntegerElement anotherIntegerElement = new IntegerElement(
				random.nextInt(Integer.MAX_VALUE));
		assertTrue(queue.offer(anotherIntegerElement));
		assertEquals(++size, queue.size());

		if (comparator.compare(integerElement, anotherIntegerElement) < 0) {
			assertEquals(integerElement.getValue(),
					((IntegerElement) queue.peek()).getValue());
		} else {
			assertEquals(anotherIntegerElement.getValue(),
					((IntegerElement) queue.peek()).getValue());
		}

	}

	/**
	 * testing with an k-element queue
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAInductionOfferWithKElementQueue() {
		//generate random k 
		int k = random.nextInt(100);
		//adding k element into the queue, verify the result
		for (int i = 0; i < k; i++) {
			E element = queue.peek();
			IntegerElement prevElement = null;
			if (element != null) {
				prevElement = (IntegerElement) queue.peek();
			}

			IntegerElement newElement = new IntegerElement(
					random.nextInt(Integer.MAX_VALUE));
			assertTrue(queue.offer(newElement));
			if (prevElement != null) {
				if (comparator.compare(prevElement, newElement) < 0) {
					assertEquals(prevElement.getValue(),
							((IntegerElement) queue.peek()).getValue());
				} else {
					assertEquals(newElement.getValue(),
							((IntegerElement) queue.peek()).getValue());
				}
			}
		}
	}

	/**
	 * testing with an k+1 - element queue
	 * @author Duy A. Nguyen (duyanh@uta.edu)
	 */
	@Test
	public void testCEAInductionOfferWithKPlusOneElementQueue() {
		int k = random.nextInt(100);
		//adding k element into the queue
		for (int i = 0; i < k; i++) {
			IntegerElement newElement = new IntegerElement(
					random.nextInt(Integer.MAX_VALUE - 1));
			queue.offer(newElement);
		}
		// store the result of k element
		IntegerElement leastIntegerElement = ((IntegerElement) queue.peek());
		int size = queue.size();
		
		IntegerElement maxIntegerElement = new IntegerElement(Integer.MAX_VALUE);
		assertTrue(queue.offer(maxIntegerElement));
		//use k's result to verify (k+1)'s result
		assertEquals(size + 1, queue.size());
		//max integer value should not be the least one
		assertTrue(maxIntegerElement.getValue() > leastIntegerElement.getValue() );

	}

}
